{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "a0d6ef1e-d8cd-40dc-8928-aa984ea6e788",
   "metadata": {},
   "outputs": [],
   "source": [
    "{-# LANGUAGE OverloadedStrings, TypeApplications #-}\n",
    "import IHaskell.Display.Widgets\n",
    "import Data.Proxy (Proxy(..))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bdf75813-33a0-4b98-a41a-35b3d88bcee9",
   "metadata": {},
   "source": [
    "# The `Link` widgets\n",
    "\n",
    "Have you ever wanted to sync some attributes between two widgets? Are you tired of setting handlers like `getValue ... >>= setValue ...`? Then today's your lucky day!\n",
    "\n",
    "The link widget allows you to sync two attributes in two different widgets. The main difference with using a handler (appart from the convenience) is that they are synced on the *frontend*. This is faster because it doesn't have to be sent to and processed by the kernel first.\n",
    "\n",
    "The `Link` widget has two fields, `Source` and `Target`, of type `WidgetFieldPair`. `WidgetFieldPair` has a constructor that receives a a widget and a field. We can link two widgets creating a `Link` widget and setting its `Source` and `Target` fields.\n",
    "\n",
    "Let's see an example linking two sliders:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "107c6fc9-c1fd-433d-8719-fa3daea8bc51",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a27260ed-9dfe-404a-9bcc-6d3da537794f",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "183bfddc-f461-482e-8640-564bf8c1c3ad",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "-- Creating the sliders\n",
    "is1 <- mkIntSlider\n",
    "is2 <- mkIntSlider\n",
    "\n",
    "-- Creating the link\n",
    "link <- mkLink\n",
    "setField @Source link (WidgetFieldPair is1 (Proxy @IntValue))\n",
    "setField @Target link (WidgetFieldPair is2 (Proxy @IntValue))\n",
    "\n",
    "-- Displaying the widget\n",
    "is1\n",
    "is2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "smart-gnome",
   "metadata": {},
   "source": [
    "If we move the first slider, the second one moves too. If we move the bottom slider, the top one moves too. Isn't it great?\n",
    "\n",
    "We can also make *directional* links. That means that if you modify the source, the target is modified too, but not viceversa.\n",
    "\n",
    "By the way, we can link any type of widget, as long as its fields have javascript-compatible types."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "f71e323b-ac72-42e5-8823-b768d6f9cb58",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "5455e23a-2c44-4481-ac98-041f45f16bb2",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "9665b5d8-a36b-4216-9d1e-120c3f24e856",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "-- We create the sliders\n",
    "fs1 <- mkFloatSlider\n",
    "fi2 <- mkIntSlider\n",
    "\n",
    "-- Making a directional link\n",
    "link <- mkDirectionalLink\n",
    "setField @Source link (WidgetFieldPair fs1 (Proxy @FloatValue))\n",
    "setField @Target link (WidgetFieldPair fi2 (Proxy @IntValue))\n",
    "\n",
    "-- Displaying the sliders\n",
    "fs1\n",
    "fi2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "stone-lightweight",
   "metadata": {},
   "source": [
    "As we expect, the bottom widget is modified when the slider in the top one changes. But the top one doesn't change if the bottom one does.\n",
    "\n",
    "But this \"making a widget and setting a source and setting a target\" troupe is a bit tiring. For this reason, we have the `jslink` and `jsdlink` functions, that create a link or directional link given two `WidgetFieldPair`s.\n",
    "\n",
    "Here we have a simple example using boolean widgets:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "91ccf115-57b4-4176-b157-0e79c27f4341",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>/* Styles used for the Hoogle display in the pager */\n",
       ".hoogle-doc {\n",
       "display: block;\n",
       "padding-bottom: 1.3em;\n",
       "padding-left: 0.4em;\n",
       "}\n",
       ".hoogle-code {\n",
       "display: block;\n",
       "font-family: monospace;\n",
       "white-space: pre;\n",
       "}\n",
       ".hoogle-text {\n",
       "display: block;\n",
       "}\n",
       ".hoogle-name {\n",
       "color: green;\n",
       "font-weight: bold;\n",
       "}\n",
       ".hoogle-head {\n",
       "font-weight: bold;\n",
       "}\n",
       ".hoogle-sub {\n",
       "display: block;\n",
       "margin-left: 0.4em;\n",
       "}\n",
       ".hoogle-package {\n",
       "font-weight: bold;\n",
       "font-style: italic;\n",
       "}\n",
       ".hoogle-module {\n",
       "font-weight: bold;\n",
       "}\n",
       ".hoogle-class {\n",
       "font-weight: bold;\n",
       "}\n",
       "\n",
       ".get-type {\n",
       "color: green;\n",
       "font-weight: bold;\n",
       "font-family: monospace;\n",
       "display: block;\n",
       "white-space: pre-wrap;\n",
       "}\n",
       ".show-type {\n",
       "color: green;\n",
       "font-weight: bold;\n",
       "font-family: monospace;\n",
       "margin-left: 1em;\n",
       "}\n",
       ".mono {\n",
       "font-family: monospace;\n",
       "display: block;\n",
       "}\n",
       ".err-msg {\n",
       "color: red;\n",
       "font-style: italic;\n",
       "font-family: monospace;\n",
       "white-space: pre;\n",
       "display: block;\n",
       "}\n",
       "#unshowable {\n",
       "color: red;\n",
       "font-weight: bold;\n",
       "}\n",
       ".err-msg.in.collapse {\n",
       "padding-top: 0.7em;\n",
       "}\n",
       "\n",
       ".highlight-code {\n",
       "white-space: pre;\n",
       "font-family: monospace;\n",
       "}\n",
       "\n",
       ".suggestion-warning { \n",
       "font-weight: bold;\n",
       "color: rgb(200, 130, 0);\n",
       "}\n",
       ".suggestion-error { \n",
       "font-weight: bold;\n",
       "color: red;\n",
       "}\n",
       ".suggestion-name {\n",
       "font-weight: bold;\n",
       "}\n",
       "\n",
       "</style><span class='get-type'>jsdlink :: WidgetFieldPair -> WidgetFieldPair -> IO DirectionalLink</span>"
      ],
      "text/plain": [
       "jsdlink :: WidgetFieldPair -> WidgetFieldPair -> IO DirectionalLink"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "0b0885c4-d023-4bba-a41c-7ca0e53a33e7",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "b21ab860-754e-469d-85fb-d6e820466311",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "5d7e4d16-5ba4-4141-9499-72324df2c594",
       "version_major": 2,
       "version_minor": 0
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    ":t jsdlink\n",
    "chk <- mkCheckBox\n",
    "tgb <- mkToggleButton\n",
    "valid <- mkValid\n",
    "\n",
    "-- The Link widget cannot be displayed, so we ignore the return value\n",
    "_ <- jslink (WidgetFieldPair chk (Proxy @BoolValue)) (WidgetFieldPair tgb (Proxy @BoolValue))\n",
    "_ <- jsdlink (WidgetFieldPair chk (Proxy @BoolValue)) (WidgetFieldPair valid (Proxy @BoolValue))\n",
    "\n",
    "chk\n",
    "tgb\n",
    "valid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "db91a9da-1075-4264-b633-0ab5a51d22f5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Haskell",
   "language": "haskell",
   "name": "haskell"
  },
  "language_info": {
   "codemirror_mode": "ihaskell",
   "file_extension": ".hs",
   "mimetype": "text/x-haskell",
   "name": "haskell",
   "pygments_lexer": "Haskell",
   "version": "9.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
